<!DOCTYPE html>
<html>
<head>
	<title>Character Visualisation</title>
	<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
	<script src="lib/cytoscape.js"></script>
	<script src="lib/arbor.js"></script>
	<script src="lib/brat-linked-vis.js"></script>	
</head>

<body>
	<h1>Characters</h1>
	<div>
    <label for="document_list">Document</label><select id="document_list" name="document_list"></select>
	<button id="refresh_document_list">Refresh</button>
	</div>
	<div id="demo" style="width: 100%; height: 500px; border: 1px black"></div>
	<script>

	options = {
    name: 'arbor',

    liveUpdate: true, // whether to show the layout as it's running
    ready: undefined, // callback on layoutready 
    stop: undefined, // callback on layoutstop
    maxSimulationTime: 4000, // max length in ms to run the layout
    fit: true, // reset viewport to fit default simulationBounds
    padding: [ 50, 50, 50, 50 ], // top, right, bottom, left
    simulationBounds: undefined, // [x1, y1, x2, y2]; [0, 0, width, height] by default
    ungrabifyWhileSimulating: true, // so you can't drag nodes during layout

    // forces used by arbor (use arbor default on undefined)
    repulsion: 1000,
    stiffness: 1000,
    friction: 0.2,
    gravity: false,
    fps: undefined,
    precision: undefined,

    // static numbers or functions that dynamically return what these
    // values should be for each element
    nodeMass: function(data)
    {
        return data.weight; // use the weight attribute in the node's data as mass
    },
  
    edgeLength: function(data)
    {
        return data.len; // use the len attribute in the node's data as mass
    },

    stepSize: 1, // size of timestep in simulation

    // function that returns true if the system is stable to indicate
    // that the layout can be stopped
    stableEnergy: function( energy ){
      var e = energy; 
      return (e.max <= 0.5) || (e.mean <= 0.3);
    }
};

// Edit this array to contain all the links you want to search for on this graph
	var required_links = [
		'http://contextus.net/ontology/ontomedia/ext/common/being#has-bond',
		'http://contextus.net/ontology/ontomedia/core/expression#shadow-of',
		'http://www.w3.org/2002/07/owl#sameAs',
		'http://contextus.net/ontology/ontomedia/core/expression#is-linked-to',
		'http://contextus.net/ontology/ontomedia/ext/common/being#is-relation-of',
		'http://contextus.net/ontology/ontomedia/ext/common/being#will-not-do',
		'http://contextus.net/ontology/ontomedia/ext/common/being#will-do',
	];


// Edit this array to contain cytoscape style objects for each link and node type
	var styles = {
		'http://contextus.net/ontology/ontomedia/ext/common/being#has-bond':
		{
			'name': 'is',
			'type': 'edge',
			'css': { 'width': 1, 'target-arrow-shape': 'triangle', 'target-arrow-color': 'black', 'line-color': 'black', 'content': ''}
		},
		'http://contextus.net/ontology/ontomedia/core/expression#shadow-of':
		{
			'name': 'shadow-of',
			'type': 'edge',
		 	'css': { 'width': 1, 'target-arrow-shape': 'triangle', 'target-arrow-color': 'grey', 'line-color': 'grey', 'content': '' }
		},
		'http://www.w3.org/1999/02/22-rdf-syntax-ns#type':
		{
			'name': 'type',
			'type': 'edge',
			'css': { 'display': 'none' }
		},
		'http://www.w3.org/2002/07/owl#sameAs':
		{
			'name': 'sameAs',
			'type': 'edge',
			'len': 5,
			'css': { 'width': 1, 'target-arrow-shape': 'triangle', 'target-arrow-color': 'black', 'line-color': 'green', 'content': '' }
		},
		'http://contextus.net/ontology/ontomedia/core/expression#Character':
		{
			'name': 'character',
			'type': 'node',
			'weight': 1.5,
			'css': { 'background-fit': 'cover', 'background-image': 'lib/assets/icons_character.png', 'background-color': 'grey', 'width' : 5, 'height': 5, 'content': 'data(name)' }
		},
		'archetype':
		{
			'name': 'archetype',
			'type': 'node',
			'weight': 0.2,
			'css': { 'background-fit': 'cover', 'background-color': 'black', 'width' : 25, 'height': 25, 'background-image': 'lib/assets/icons_character.png', 'content': 'data(name)' }
		},
	};

// Edit this array to contain a zoomcharts style object for anything else (currently this is mainly for events)
//	var default_style = { 'fillColor': 'green', 'radius' : 20 };

	var default_node = { 'content': 'data(name)', 'background-color': 'green', 'shape': 'circle', 'width' : 5, 'height': 5 };
	var default_edge = { 'content': 'data(label)', 'background-color': 'black', 'width' : 1 };
	

// Edit this to point to your public SPARQL endpoint
sparql_root = 'http://brat.kludge.co.uk/sparql/';

// Don't edit anything past here
	var data;
	var chart = null;

	$( document ).ready(function() {
		if (chart == null)
		{
			/*chart = new NetChart({
	        	container: document.getElementById("demo"),
	        	height: 600,
	        });*/
	        
	        chart = cytoscape({
                container: document.getElementById('demo'),
            });
 		}

        $('#refresh_document_list').click(function() {
			refresh_document_list(sparql_root, true);
		});
		
	    refresh_document_list(sparql_root, true);
	    
	});
	
	$('#document_list').change(function () {
		refresh_page();
	});
	
	function refresh_page()
	{
	    update_graph();
	}	

	String.prototype.hashCode = function(){
	    var hash = 0;
	    if (this.length == 0) return hash;
	    for (var i = 0; i < this.length; i++) {
        	var character = this.charCodeAt(i);
        	hash = ((hash<<5)-hash)+character;
        	hash = hash & hash; // Convert to 32bit integer
    	}
    	return hash;
	}

	function componentToHex ( c ) {
    	var hex = c.toString(16);
    	return hex.length == 1 ? "0" + hex : hex;
	}

	function rgbToHex ( r, g, b ) {
	    return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
	}

	function reverse ( s )
	{
	    return s.split("").reverse().join("");
	}

    function colorFromURI ( uri ) {
        //query_data.g.value
		var hash = reverse(uri).hashCode();
		var red = (hash & 0xFF0000) >> 16;
		var green = (hash & 0x00FF00) >> 8;
		var blue = hash & 0x0000FF;

        return componentToHex(red) + componentToHex(green) + componentToHex(blue);
    }

                    
					//var ref_style = $.extend({'name': 'query_data.g.value + "#" + ref_label', 'type':'node', 'css': default_node});

	function update_graph ( )
	{
	    graph_url = $('#document_list').val();
	    
	    // Edit this array to contain the query string, with __GRAPH__ and __LINK_TYPE__ as placeholders
	var query_string = 'PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> \
		PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> ';
		
		if(graph_url == '')
            query_string += 'SELECT  DISTINCT * WHERE { GRAPH ?g {';
        else
            query_string += 'SELECT  DISTINCT * WHERE {GRAPH <__GRAPH__> {';

		query_string += '?ref rdf:type <http://contextus.net/ontology/ontomedia/core/expression#Character> \
		OPTIONAL {?ref rdfs:label ?ref_label }. \
		OPTIONAL {?ref <http://www.w3.org/2002/07/owl#sameAs> ?local. \
		?local <http://contextus.net/ontology/ontomedia/core/expression#shadow-of> ?global}. \
		OPTIONAL {?ref <http://contextus.net/ontology/ontomedia/ext/common/trait#has-trait> [rdf:type <http://contextus.net/ontology/ontomedia/ext/common/trait#Name>; \
		<http://contextus.net/ontology/ontomedia/ext/common/trait#has-name> ?ref_name]}. \
		OPTIONAL {?ref <http://contextus.net/ontology/ontomedia/ext/common/trait#has-trait> [rdf:type <http://contextus.net/ontology/ontomedia/ext/common/trait#link>; \
		<http://contextus.net/ontology/ontomedia/ext/common/being#has-bond> [rdf:type ?bond_type; ?bond_rel ?bonded]] \
		}. \
		}}';
		
//MINUS {_:blankNode <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> ?bonded}	
	    
	    data = { "nodes": [], "edges":[] };
	    color_classes = {};
	    
	    query_string = query_string.replace('__GRAPH__', graph_url)

		$.ajax({
    		url: sparql_root,
    		dataType: 'json',
		    data: {
        		query: query_string,
		    },

		    success: function( response ) {

		    	// Find all the individual nodes first
		    	$.each(response.results.bindings, function (index, query_data) {
		    	
		    		var ref_id = query_data.ref.value;
		    		var global_id = query_data.global.value;
		    		var local_id = query_data.local.value;
		    		var ref_label = ref_id;
		    		var parts = [];
		    		
		    		if (ref_id.indexOf('/') != -1)
		    		{
		    		    parts = ref_id.split('/');
		    		    ref_label = parts[parts.length-1];
		    		} 
		    		if ('value' in query_data.ref_label) ref_label = query_data.ref_label.value;
		    		//if ('value' in query_data.ref_name) ref_label = query_data.ref_name.value;
		    		
		    		var local_label = ref_label;
		    		var global_label = ref_label;
		    		
		    		if ('value' in query_data.local) 
		    		{
		    		    local_label = local_id;
		    		    
		    		    if (local_label.indexOf('/') != -1)
		    		    {
		    		        parts = local_id.split('/');
		    		        local_label = parts[parts.length-1];
		    		    }
		    		}
		    		
		    		if ('value' in query_data.global)
		    		{
		    		    global_label = global_id;
		    		    
		    		    if (global_label.indexOf('/') != -1)
		    		    {
		    		        parts = global_id.split('/')
		    		        global_label = parts[parts.length-1];
		    		    }
		    		}

		    		var foundR = 0;
		    		var foundL = 0;
		    		var foundG = 0;
		    		
		    		for (i = 0; i < data['nodes'].length; i++)
		    		{
		    			if (data['nodes'][i]['id'] == ref_id) foundR = 1;
		    			if (data['nodes'][i]['id'] == local_id) foundL = 1;
		    			if (data['nodes'][i]['id'] == global_id) foundG = 1;
		    		}

                    if (query_data.g)
                        color = colorFromURI(query_data.g.value);
                    else
                        color = colorFromURI('graph_url')
                        
                    colorClass = 'color_' + color;					
					color_classes[colorClass] = {'background-color': '#' + color, 'background-fit': 'cover', 'height': 10, 'width': 10, 'content': 'data(name)' }
					
		    		//if (foundR == 0) data['nodes'].push({'id': ref_id, style: ref_style});
		    		
		    		if (foundR == 0 && ref_label != undefined && ref_id.indexOf("shared") == -1) data['nodes'].push({'data': {'id': ref_id, 'name': ref_label}, 'classes': colorClass, 'weight': 1});
		    		if (foundL == 0 && local_id != undefined) data['nodes'].push({'data': {'id': local_id, 'name': local_label}, 'classes': colorClass, 'weight': 0.8});
		    		if (foundG == 0 && global_id != undefined) data['nodes'].push({'data': {'id': global_id, 'name': global_label}, 'classes': styles['archetype']['name']});
	    		
		    	});

		    	$.each(response.results.bindings, function (index, query_data) {
		    		var ref_id = query_data.ref.value;
		    		
/*				
					if ('value' in query_data.bonded)
					{
					    var link_id = "link_" + ref_id + '_' 
		    			
		    			if ('value' in query_data.bonded)
		    			    link_id += query_data.bonded.value + '_'
		    			
		    			if ('value' in query_data.bond_type)    
		    			    link_id += query_data.bond_type.value;
					
					    if (styles[required_links['http://contextus.net/ontology/ontomedia/ext/common/being#has-bond']] != undefined)
		    		    	data['edges'].push({'data': {"id": link_id, "source": ref_id, "target": query_data.bonded.value},'classes': styles[required_links['http://contextus.net/ontology/ontomedia/ext/common/being#has-bond']]['name']});
		    			else
                            data['edges'].push({'data': {"id": link_id, "source": ref_id, "target": query_data.bonded.value}});
		    		}
*/
					if ('value' in query_data.local)
					{
		    			var link_id = "link_" + ref_id + '_' 
		    			
		    			if ('value' in query_data.local)
		    			    link_id += query_data.local.value + '_'
		    			
		    			if ('value' in query_data.bond_type)    
		    			    link_id += query_data.bond_type.value;
		    			
		    			if (styles['http://www.w3.org/2002/07/owl#sameAs'] != undefined)
		    		    	data['edges'].push({'data': {"id": link_id, "source": ref_id, "target": query_data.local.value}, 'classes': styles['http://www.w3.org/2002/07/owl#sameAs']['name']});
		    			else
		    			    data['edges'].push({'data': {"id": link_id, "source": ref_id, "target": query_data.local.value}});
		    		}
/*	*/
					if ('value' in query_data.global && 'value' in query_data.local)
					{		    			
		    			var link_id = "link_" + query_data.local.value + '_' 
		    			
		    			if ('value' in query_data.global)
		    			    link_id += query_data.global.value + '_'
		    			
		    			if ('value' in query_data.bond_type)    
		    			    link_id += query_data.bond_type.value;
		    			
		    			if (styles['http://contextus.net/ontology/ontomedia/core/expression#shadow-of'] != undefined)
		    		    	data['edges'].push({'data': {"id": link_id, "source": query_data.local.value, "target": query_data.global.value}, 'classes': styles['http://contextus.net/ontology/ontomedia/core/expression#shadow-of']['name']});
		    			else
		    			    data['edges'].push({'data': {"id": link_id, "source": query_data.local.value, "target": query_data.global.value}});
		    			
		    	        
		    		}	
		    		
		    		
		    		



	    			    		

		    	});

	    		chart = cytoscape({
                container: document.getElementById('demo'),
                });
                chart.style().selector('edge').css(default_edge);
    		    chart.style().selector('node').css(default_node);
                $.each(styles, function (style_match, style_details)
                {
                    selector = style_details['type'] + '.' + style_details['name'];
                    chart.style().selector(selector).css(style_details['css']);
                });
                $.each(color_classes, function (style_class, details)
                {
                    chart.style().selector('node.' + style_class).css(details);
                });
    		    chart.add(data);
    		    chart.layout(options);
    		}
		});

    }




</script>
</body>
</html>